package com.mrd.gtimpframe.service;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.transaction.Transactional;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.PropertyFilter;
import com.github.pagehelper.PageInfo;
import com.mrd.framework.utils.HttpUtils;
import com.mrd.framework.utils.SpringUtils;
import com.mrd.gtimp.business.entity.Declaration;
import com.mrd.gtimp.business.repository.DeclarationDao;
import com.mrd.gtimpframe.entity.OriginalDeclaration;
import com.mrd.gtimpframe.repository.OriginalDeclarationMapper;
import com.mrd.util.LoginUtil;
import com.mrd.util.StringUtils;
import com.mrd.util.html2pdf.PhantomjsTools;

/**
 * Created by dengdc on 2017/7/17.
 */
@Service
public class OriginalDeclarationBiz {
  @Resource
  private OriginalDeclarationMapper originalDeclarationMapper;
  @Resource
  private DeclarationDao declarationDao;

  private static LinkedBlockingQueue<TransferDeclarationToPdfTask> transferQueue =
      new LinkedBlockingQueue<TransferDeclarationToPdfTask>();

  public static Map<String, OriginalDeclaration> waittingMap = new HashMap<>();

  private static ExecutorService transferServer = Executors.newCachedThreadPool();

  /**
   * 将报关单保存备份
   * 
   * @param did 报关单ID
   */
  @Transactional
  public void saveDeclarationAsOriginal(String did) {
    Declaration declaration = declarationDao.getOne(did);
    OriginalDeclaration originalDeclaration = new OriginalDeclaration();
    originalDeclaration.setId(UUID.randomUUID().toString());
    originalDeclaration.setDid(did);
    originalDeclaration.setBusinessNo(declaration.getBusinessId());
    originalDeclaration.setJsonData(getDeclarationJsonString(declaration));
    originalDeclaration.setCreateTime(new Date());
    originalDeclaration.setCreator(LoginUtil.getLoginUser().getId());
    try {
      originalDeclarationMapper.insertSelective(originalDeclaration);
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }

    createPdf(originalDeclaration);
  }

  /**
   * 优化，添加多一个方法，用户多线程中操作，不能获取userId
   * 
   * @author han
   * @param did
   * @param userId
   */
  @Transactional
  public void saveDeclarationAsOriginalByDidiUser(String did, String userId) {
    Declaration declaration = declarationDao.getOne(did);
    OriginalDeclaration originalDeclaration = new OriginalDeclaration();
    originalDeclaration.setId(UUID.randomUUID().toString());
    originalDeclaration.setDid(did);
    originalDeclaration.setBusinessNo(declaration.getBusinessId());
    originalDeclaration.setJsonData(getDeclarationJsonString(declaration));
    originalDeclaration.setCreateTime(new Date());
    originalDeclaration.setCreator(userId);
    try {
      originalDeclarationMapper.insertSelective(originalDeclaration);
    } catch (Exception e) {
      e.printStackTrace();
      throw e;
    }

    createPdf(originalDeclaration);
  }

  /**
   * 获取报关单json字符串
   * 
   * @param sourceDeclaration 报关单实体
   */
  @Transactional
  private String getDeclarationJsonString(Declaration sourceDeclaration) {

    // 设置在转成json时候忽略的属性
    Set<String> excludeProperties = new HashSet<>();
    excludeProperties.add("depts");
    excludeProperties.add("roles");
    excludeProperties.add("owner");
    excludeProperties.add("declaration");
    excludeProperties.add("materials");
    PropertyFilter propertyFilter = new PropertyFilter() {
      @Override
      public boolean apply(Object object, String name, Object value) {
        return !excludeProperties.contains(name);
      }
    };

    // 转换成json 附加日期格式
    String jsonString = JSONObject.toJSONString(sourceDeclaration, propertyFilter);
    System.out.println(jsonString);
    return jsonString;
  }

  private void createPdf(OriginalDeclaration originalDeclaration) {
    TransferDeclarationToPdfTask task = new TransferDeclarationToPdfTask();
    task.setOriginalDeclaration(originalDeclaration);
    transferServer.execute(task);
  }

  /**
   * 根据报关单ID，从备份中还原报关单对象
   * 
   * @param originalId 备份报关单ID
   * @return 备份时存的报关单信息
   */
  public Declaration getDeclarationFromOriginalId(String originalId) {
    if (StringUtils.isEmpty(originalId)) {
      return new Declaration();
    }
    OriginalDeclaration originalDeclaration =
        originalDeclarationMapper.selectByPrimaryKey(originalId);
    String jsonString = originalDeclaration.getJsonData();
    return JSONObject.parseObject(jsonString, Declaration.class);
  }

  /**
   * 列表页面数据
   * 
   * @param page 分页信息
   * @param params 查询参数
   * @return
   */
  public Page<Map<String, Object>> listPageData(Pageable page, Map<String, Object> params) {
    List<Map<String, Object>> pageMapList = originalDeclarationMapper.listSelective(params);
    PageInfo<Map<String, Object>> pageInfo = new PageInfo(pageMapList);
    return new PageImpl<Map<String, Object>>(pageMapList, page, pageInfo.getTotal());
  }

  /**
   * 下载pdf文件
   * 
   * @param response
   * @param dateStr
   * @param pdfName
   * @param originalId
   */
  public void downloadPdfFile(HttpServletResponse response, String dateStr, String pdfName,
      String originalId) {
    String year = dateStr.substring(0, 4);
    String month = dateStr.substring(5, 7);
    String day = dateStr.substring(8, 10);
    String baseDir = SpringUtils.getConfigProperties("file.declaration.pdf.dir");
    File pdfFile = new File(baseDir + File.separator + year + File.separator + month
        + File.separator + day + File.separator + pdfName);
    if (!pdfFile.exists()) {
      originalId = HttpUtils.getParam("originalId");
      OriginalDeclaration originalDeclaration =
          originalDeclarationMapper.selectByPrimaryKey(originalId);
      createPdfRightNow(originalDeclaration);
      if (pdfFile.exists()) {
        printFile(response, pdfFile);
      }
    } else {
      printFile(response, pdfFile);
    }
  }

  /**
   * 输出文件
   * 
   * @param file 要下载的文件
   */
  private void printFile(HttpServletResponse response, File file) {
    try (InputStream fis = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(fis);
        OutputStream fos = response.getOutputStream();
        BufferedOutputStream bos = new BufferedOutputStream(fos)) {
      response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
      response.setHeader("Content-Length", "" + file.length());
      int byteRead;
      byte[] buffer = new byte[2048];
      while ((byteRead = bis.read(buffer, 0, 2048)) != -1) {
        bos.write(buffer, 0, byteRead);
      }
      bos.flush();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  @org.springframework.transaction.annotation.Transactional
  public void createPdfRightNow(OriginalDeclaration originalDeclaration) {
    String fileName = PhantomjsTools.execUrlToPdf(
        SpringUtils.getConfigProperties("phantomjs.screenshot.order.url") + "?originalId="
            + originalDeclaration.getId(),
        originalDeclaration.getPdfName(),
        new SimpleDateFormat("yyyyMMdd").format(originalDeclaration.getCreateTime()));
    originalDeclaration.setPdfName(fileName);
    SpringUtils.getBean(OriginalDeclarationMapper.class)
        .updateByPrimaryKeySelective(originalDeclaration);
  }
}


class TransferDeclarationToPdfTask implements Runnable {
  private OriginalDeclaration originalDeclaration;

  void setOriginalDeclaration(OriginalDeclaration originalDeclaration) {
    this.originalDeclaration = originalDeclaration;
    OriginalDeclarationBiz.waittingMap.put(originalDeclaration.getDid(), originalDeclaration);
  }

  @Override
  public void run() {
    String fileName = PhantomjsTools.execUrlToPdf(
        SpringUtils.getConfigProperties("phantomjs.screenshot.order.url") + "?originalId="
            + originalDeclaration.getId(),
        originalDeclaration.getPdfName(),
        new SimpleDateFormat("yyyyMMdd").format(originalDeclaration.getCreateTime()));
    originalDeclaration.setPdfName(fileName);
    SpringUtils.getBean(OriginalDeclarationMapper.class)
        .updateByPrimaryKeySelective(originalDeclaration);
  }
}
